
#include "RtBase.h"
#include "RtAcceptorThreadProxy.h"

CRtAcceptorThreadProxy::
CRtAcceptorThreadProxy(CRtConnectionManager::CType aType,
					   CRtThread *aThreadNetwork,
					   CRtThread *aThreadUser)
	: CRtAcceptorConnectorSinkThreadProxyT<CRtAcceptorThreadProxy>(this)
	, m_pThreadUser(aThreadUser)
	, m_pThreadNetwork(aThreadNetwork)
	, m_Type(aType)
{
	if (!m_pThreadNetwork) {
		m_pThreadNetwork = CRtThreadManager::Instance()->GetThread(CRtThreadManager::TT_NETWORK);
		RT_ASSERTE(m_pThreadNetwork);
	}
	if (!m_pThreadUser) {
		m_pThreadUser = CRtThreadManager::Instance()->GetThread(CRtThreadManager::TT_CURRENT);
		RT_ASSERTE(m_pThreadUser);
	}
	RT_ASSERTE(m_pThreadNetwork != m_pThreadUser);
}

CRtAcceptorThreadProxy::~CRtAcceptorThreadProxy()
{
	// the current thread is network thread due to <m_pAcceptorActual>.
//	RT_ASSERTE(CRtThreadManager::IsEqualCurrentThread(m_pThreadNetwork->GetThreadId()));
}

DWORD CRtAcceptorThreadProxy::AddReference()
{
	return CRtReferenceControlMutilThread::AddReference();
}

DWORD CRtAcceptorThreadProxy::ReleaseReference()
{
	return CRtReferenceControlMutilThread::ReleaseReference();
}

void CRtAcceptorThreadProxy::OnReferenceDestory()
{
	// this assert helps to debug that the upper layer 
	// didn't call StopListen() before.
	RT_ASSERTE(CRtStopFlag::m_bStoppedFlag);

	// only the user thread can delete this due to <m_pAcceptorActual>,
	// so we have to post delete event to the network thread.
	if (CRtThreadManager::IsEqualCurrentThread(m_pThreadUser->GetThreadId())) {
//		RT_INFO_TRACE_THIS("CRtAcceptorThreadProxy::OnReferenceDestory,"
//			" post delete event to the network thread");
		CRtEventDeleteT<CRtAcceptorThreadProxy> *pEventDelete;
		pEventDelete = new CRtEventDeleteT<CRtAcceptorThreadProxy>(this);
		if (pEventDelete)
			pEventDelete->Launch(m_pThreadNetwork);
	}
	else {
		delete this;
	}
}

BOOL CRtAcceptorThreadProxy::IsConnector()
{
	return FALSE;
}

RtResult CRtAcceptorThreadProxy::
StartListen(IRtAcceptorConnectorSink *aSink, const CRtInetAddr &aAddrListen)
{
	RT_ASSERTE(CRtThreadManager::IsEqualCurrentThread(m_pThreadUser->GetThreadId()));
	RT_ASSERTE_RETURN(CRtStopFlag::IsFlagStopped(), RT_ERROR_ALREADY_INITIALIZED);

	// Don't check m_pAcceptorActual due to its operations are all in network thread.
//	RT_ASSERTE(!m_pAcceptorActual);
	
//	RT_INFO_TRACE_THIS("CRtAcceptorThreadProxy::StartListen,"
//		" aSink=" << aSink <<
//		" addr=" << aAddrListen.GetIpDisplayName() << 
//		" port=" << aAddrListen.GetPort());

	RT_ASSERTE(!CRtAcceptorConnectorSinkThreadProxyT<CRtAcceptorThreadProxy>::m_pSinkActual);
	RT_ASSERTE_RETURN(aSink, RT_ERROR_INVALID_ARG);
	CRtAcceptorConnectorSinkThreadProxyT<CRtAcceptorThreadProxy>::ResetSink(aSink);
	
	RtResult rv = RT_ERROR_OUT_OF_MEMORY;
	CEventStartListen *pEvent = new CEventStartListen(this, this, aAddrListen);
	if (pEvent) {
		// we must use SendEvent() to get the listen result.
		rv = m_pThreadNetwork->GetEventQueue()->SendEvent(pEvent);
	}

	if (RT_FAILED(rv)) {
		RT_WARNING_TRACE_THIS("CRtAcceptorThreadProxy::StartListen, SendEvent() failed.");
		CRtAcceptorConnectorSinkThreadProxyT<CRtAcceptorThreadProxy>::ResetSink(NULL);
	}
	else 
		CRtStopFlag::SetStartFlag();
	return rv;
}

RtResult CRtAcceptorThreadProxy::StopListen(RtResult aReason)
{
	RT_ASSERTE(CRtThreadManager::IsEqualCurrentThread(m_pThreadUser->GetThreadId()));

	if (CRtStopFlag::IsFlagStopped())
		return RT_OK;
	CRtStopFlag::SetStopFlag();

//	RT_INFO_TRACE_THIS("CRtAcceptorThreadProxy::StopListen, aReason=" << aReason);
	
	CRtAcceptorConnectorSinkThreadProxyT<CRtAcceptorThreadProxy>::ResetSink(NULL);

	CEventStopListen *pEvent = new CEventStopListen(this, aReason);
	if (pEvent) {
		// use PostEvent() in spite of network thread is main thread or serparator thread.
		return m_pThreadNetwork->GetEventQueue()->PostEvent(pEvent);
	}
	else 
		return RT_ERROR_OUT_OF_MEMORY;
}


//////////////////////////////////////////////////////////////////////
// class CEventStartListen
//////////////////////////////////////////////////////////////////////

CEventStartListen::
CEventStartListen(CRtAcceptorThreadProxy *aThreadProxy, 
				  IRtAcceptorConnectorSink *aSink, 
				  const CRtInetAddr &aAddrListen)
	: m_pOwnerThreadProxy(aThreadProxy)
	, m_pSink(aSink)
	, m_addrListen(aAddrListen)
{
}

CEventStartListen::~CEventStartListen()
{
}

RtResult CEventStartListen::OnEventFire()
{
	RT_ASSERTE(CRtThreadManager::IsEqualCurrentThread(
		m_pOwnerThreadProxy->m_pThreadNetwork->GetThreadId()));

	// we must new actual acceptor in the network thread.
	RT_ASSERTE(!m_pOwnerThreadProxy->m_pAcceptorActual);
	CRtConnectionManager::CType type = m_pOwnerThreadProxy->m_Type;

	RtResult rv = CRtConnectionManager::Instance()->CreateConnectionServer(
		type, 
		m_pOwnerThreadProxy->m_pAcceptorActual.ParaOut());
	if (RT_FAILED(rv)) {
		RT_ERROR_TRACE_THIS("CEventStartListen::OnEventFire,"
			" can't create acceptor in the network thread. rv=" << rv);
		return rv;
	}

	rv = m_pOwnerThreadProxy->
		m_pAcceptorActual->StartListen(m_pSink, m_addrListen);
	if (RT_FAILED(rv))
		m_pOwnerThreadProxy->m_pAcceptorActual = NULL;
	return rv;
}


//////////////////////////////////////////////////////////////////////
// class CEventStopListen
//////////////////////////////////////////////////////////////////////

CEventStopListen::
CEventStopListen(CRtAcceptorThreadProxy *aConnectorThreadProxy,
				 RtResult aReason)
	: m_pOwnerThreadProxy(aConnectorThreadProxy)
	, m_Reason(aReason)
{
	RT_ASSERTE(m_pOwnerThreadProxy);
}

CEventStopListen::~CEventStopListen()
{
}

RtResult CEventStopListen::OnEventFire()
{
	RT_ASSERTE(CRtThreadManager::IsEqualCurrentThread(
		m_pOwnerThreadProxy->m_pThreadNetwork->GetThreadId()));

	RT_ASSERTE(m_pOwnerThreadProxy->m_pAcceptorActual);
	if (m_pOwnerThreadProxy->m_pAcceptorActual) {
		RtResult rv = m_pOwnerThreadProxy->m_pAcceptorActual->StopListen(m_Reason);
		m_pOwnerThreadProxy->m_pAcceptorActual = NULL;
		return rv;
	}
	else
		return RT_ERROR_NULL_POINTER;
}
